﻿using System;
using CommonFramework.FixMath.LookupTable;

namespace CommonFramework.FixMath
{
    public static class FMathUtil
    {
        #region 常量

        public static readonly FFloat PI = FFloat.FromRawInt(3142);
        public static readonly FFloat HalfPI = FFloat.FromRawInt(1571);
        public static readonly FFloat TwoPI = FFloat.FromRawInt(6283);
        public static readonly FFloat Rad2Deg = 180 / PI;
        public static readonly FFloat Deg2Rad = PI / 180;

        #endregion

        #region 三角函数

        public static FFloat Sin(FFloat radian)
        {
            int index = FLookupTableSinCos.GetIndex(radian);
            return FFloat.FromRawLong((long) FLookupTableSinCos.SinTable[index] / 10);
        }
        public static FFloat Asin(FFloat val)
        {
            
            int num = (int)(val.RawVal() * (long)FLookupTableASin.HALF_COUNT / FFloat.Precision) +
                      FLookupTableASin.HALF_COUNT;
            num = Clamp(num, 0, FLookupTableASin.COUNT);
            return FFloat.FromRawLong((long) FLookupTableASin.Table[num] / 10);
        }
        public static FFloat Cos(FFloat radians)
        {
            int index = FLookupTableSinCos.GetIndex(radians);
            return FFloat.FromRawLong((long) FLookupTableSinCos.CosTable[index] / 10);
        }
        public static FFloat Acos(FFloat val)
        {
            int num = (int)(val.RawVal() * (long)FLookupTableACos.HALF_COUNT / FFloat.Precision) +
                      FLookupTableACos.HALF_COUNT;
            num = Clamp(num, 0, FLookupTableACos.COUNT);
            return FFloat.FromRawLong((long) FLookupTableACos.Table[num] / 10);
        }
        public static void SinCos(out FFloat sin, out FFloat cos, FFloat radians)
        {
            int index = FLookupTableSinCos.GetIndex(radians);
            sin = FFloat.FromRawLong((long) FLookupTableSinCos.SinTable[index] / 10);
            cos = FFloat.FromRawLong((long) FLookupTableSinCos.CosTable[index] / 10);
        }
        public static FFloat Atan2(FFloat y, FFloat x)
        {
            return Atan2(y.RawVal(), x.RawVal());
        }
        public static FFloat Atan2(int y, int x)
        {
            if (x == 0)
            {
                if (y > 0) return HalfPI;
                else if (y < 0) { return -HalfPI; }
                else return FFloat.Zero;
            }
            if (y == 0)
            {
                if (x > 0) return FFloat.Zero;
                else if (x < 0) { return PI; }
                else return FFloat.Zero;
            }

            int num;
            int num2;
            if (x < 0)
            {
                if (y < 0)
                {
                    x = -x;
                    y = -y;
                    num = 1;
                }
                else
                {
                    x = -x;
                    num = -1;
                }

                num2 = -31416;
            }
            else
            {
                if (y < 0)
                {
                    y = -y;
                    num = -1;
                }
                else
                {
                    num = 1;
                }

                num2 = 0;
            }

            int dIM = FLookupTableATan2.DIM;
            long num3 = (long)(dIM - 1);
            long b = (long)((x >= y) ? x : y);
            int num4 = (int)((long)x * num3 / b);
            int num5 = (int)((long)y * num3 / b);
            int num6 = FLookupTableATan2.Table[num5 * dIM + num4];

            return FFloat.FromRawLong((long)((num6 + num2) * num) / 10);
        }

        #endregion

        #region 开平方根

        public static uint Sqrt32(uint a)
        {
            uint num = 0u;
            uint num2 = 0u;
            for (int i = 0; i < 16; i++)
            {
                num2 <<= 1;
                num <<= 2;
                num += a >> 30;
                a <<= 2;
                if (num2 < num)
                {
                    num2 += 1u;
                    num -= num2;
                    num2 += 1u;
                }
            }

            return num2 >> 1 & 65535u;
        }
        public static ulong Sqrt64(ulong a)
        {
            ulong num = 0uL;
            ulong num2 = 0uL;
            for (int i = 0; i < 32; i++)
            {
                num2 <<= 1;
                num <<= 2;
                num += a >> 62;
                a <<= 2;
                if (num2 < num)
                {
                    num2 += 1uL;
                    num -= num2;
                    num2 += 1uL;
                }
            }

            return num2 >> 1 & 0xffffffffu;
        }
        public static int Sqrt(int a)
        {
            if (a <= 0)
            {
                return 0;
            }
            return (int)Sqrt32((uint)a);
        }
        public static int Sqrt(long a)
        {
            if (a <= 0L)
            {
                return 0;
            }

            if (a <= (long)(0xffffffffu))
            {
                return (int)Sqrt32((uint)a);
            }

            return (int)Sqrt64((ulong)a);
        }
        public static FFloat Sqrt(FFloat a)
        {
            if (a.RawVal() <= 0)
            {
                return FFloat.Zero;
            }

            return FFloat.FromRawLong(Sqrt((long) a.RawVal() * FFloat.Precision));
        }

        #endregion

        #region 限定数值范围

        public static int Clamp(int value, int min, int max)
        {
            if (value < min)
                return min;
            if (value > max)
                return max;
            return value;
        }
        public static long Clamp(long value, long min, long max)
        {
            if (value < min)
                return min;
            if (value > max)
                return max;
            return value;
        }
        public static FFloat Clamp(FFloat value, FFloat min, FFloat max)
        {
            if (value < min)
            {
                return min;
            }
            if (value > max)
            {
                return max;
            }
            return value;
        }
        public static FFloat Clamp01(FFloat a)
        {
            if (a < FFloat.Zero)
            {
                return FFloat.Zero;
            }

            if (a > FFloat.One)
            {
                return FFloat.One;
            }

            return a;
        }

        public static int Max(int a, int b)
        {
            return (a <= b) ? b : a;
        }
        public static int Max(params int[] values)
        {
            int length = values.Length;
            if (length == 0)
                return 0;
            int num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] > num)
                    num = values[index];
            }
            return num;
        }
        public static long Max(long a, long b)
        {
            return (a <= b) ? b : a;
        }
        public static long Max(params long[] values)
        {
            int length = values.Length;
            if (length == 0)
                return 0;
            long num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] > num)
                    num = values[index];
            }
            return num;
        }
        public static FFloat Max(FFloat a, FFloat b)
        {
            return FFloat.FromRawInt(Max(a.RawVal(), b.RawVal()));
        }
        public static FFloat Max(params FFloat[] values)
        {
            int length = values.Length;
            if (length == 0)
                return FFloat.Zero;

            var num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] > num)
                    num = values[index];
            }
            return num;
        }

        public static int Min(int a, int b)
        {
            return (a < b) ? a : b;
        }
        public static int Min(params int[] values)
        {
            int length = values.Length;
            if (length == 0)
                return 0;
            int num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] < num)
                    num = values[index];
            }
            return num;
        }
        public static long Min(long a, long b)
        {
            return (a < b) ? a : b;
        }
        public static long Min(params long[] values)
        {
            int length = values.Length;
            if (length == 0)
                return 0;
            long num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] < num)
                    num = values[index];
            }
            return num;
        }
        public static FFloat Min(FFloat a, FFloat b)
        {
            return FFloat.FromRawInt(Min(a.RawVal(), b.RawVal()));
        }
        public static FFloat Min(params FFloat[] values)
        {
            int length = values.Length;
            if (length == 0)
                return FFloat.Zero;

            var num = values[0];
            for (int index = 1; index < length; ++index)
            {
                if (values[index] < num)
                    num = values[index];
            }
            return num;
        }
        #endregion

        #region 数值符号

        public static int Sign(FFloat val)
        {
            return Math.Sign(val.RawVal());
        }
        public static bool SameSign(FFloat a, FFloat b)
        {
            int rawA= a.RawVal();
            int rawB = b.RawVal();

            if (rawA != 0 && rawB != 0)
                return (long) rawA * rawB > 0;
            else if (rawA == 0 && rawB == 0)
                return true;
            else
                return false;
        }
        public static int Abs(int val)
        {
            if (val < 0)
            {
                return -val;
            }
            return val;
        }
        public static long Abs(long val)
        {
            if (val < 0L)
            {
                return -val;
            }
            return val;
        }
        public static FFloat Abs(FFloat val)
        {
            if (val.RawVal() < 0)
                return -val;
            return val;
        }

        #endregion

        #region 数值舍入

        public static FFloat Round(FFloat val)
        {
            if (val <= 0)
            {
                var remainder = (-val.RawVal()) % FFloat.Precision;
                if (remainder > FFloat.HalfPrecision)
                {
                    return FFloat.FromRawLong(val.RawVal() + remainder - FFloat.Precision);
                }
                else
                {
                    return FFloat.FromRawLong(val.RawVal() + remainder);
                }
            }
            else
            {
                var remainder = (val.RawVal()) % FFloat.Precision;
                if (remainder > FFloat.HalfPrecision)
                {
                    return FFloat.FromRawLong(val.RawVal() - remainder + FFloat.Precision);
                }
                else
                {
                    return FFloat.FromRawLong(val.RawVal() - remainder);
                }
            }
        }
        public static FFloat Floor(FFloat val)
        {
            int raw = val.RawVal();
            if (raw > 0)
            {
                raw /= FFloat.Precision;
            }
            else
            {
                if (raw % FFloat.Precision == 0)
                {
                    raw /= FFloat.Precision;
                }
                else
                {
                    raw = raw / FFloat.Precision - 1;
                }
            }
            return FFloat.FromRawLong(raw);
        }
        public static FFloat Ceil(FFloat val)
        {
            int raw = val.RawVal();
            if (raw < 0)
            {
                raw /= FFloat.Precision;
            }
            else
            {
                if (raw % FFloat.Precision == 0)
                {
                    raw /= FFloat.Precision;
                }
                else
                {
                    raw = raw / FFloat.Precision + 1;
                }
            }
            return FFloat.FromRawLong(raw);
        }
        public static int FloorToInt(FFloat a)
        {
            var raw = a.RawVal();
            if (raw < 0)
            {
                raw = raw - FFloat.Precision + 1;
            }
            return raw / FFloat.Precision;
        }
        public static bool IsPowerOfTwo(int x)
        {
            return (x & x - 1) == 0;
        }
        public static int CeilPowerOfTwo(int x)
        {
            x--;
            x |= x >> 1;
            x |= x >> 2;
            x |= x >> 4;
            x |= x >> 8;
            x |= x >> 16;
            x++;
            return x;
        }

        #endregion

        #region 插值

        public static FFloat Lerp(FFloat a, FFloat b, FFloat f)
        {
            return FFloat.FromRawLong(((long)(b.RawVal() - a.RawVal()) * f.RawVal()) / FFloat.Precision + a.RawVal());
        }
        public static FFloat InverseLerp(FFloat a, FFloat b, FFloat value)
        {
            if (a != b)
                return Clamp01((value - a) / (b - a));
            return FFloat.Zero;
        }

        #endregion
    }
}